/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tp1_banda_cordi_vasquez;

import java.util.NoSuchElementException;

/**
 *
 * @author joseutn
 */
public class LinkedList implements Cloneable {

    private Node header = new Node(null, null, null);
    private int size;
    private boolean order = true;

    public LinkedList() {
        header.next = header.previuos = header;
    }

    public void add(int index, Comparable add){
        if(index<0||index>size)
            throw new IndexOutOfBoundsException();
        Node detrasDe= node(index);
        addBefore(add,detrasDe);        
    }

    public boolean add(Comparable c) {
        return (null != (addBefore(c, header)));
    }

    public void addFirst(Comparable c) {
        addBefore(c, header.next);
    }

    public void addLast(Comparable c) {
        addBefore(c, header);
    }

    private void orden(Comparable c, Node n) {
        try {
            //Por defecto orden ascendente por el camino del next
            int ord = n.info.compareTo(c);
            if (ord == 0) {
                order = true;
            } else {
                if (ord < 0) {
                    order = false;
                } else {
                    order = true;
                }
            }
        }//Paso
        catch (NullPointerException e) {
            ;
        }
    }

    private Node addBefore(Comparable c, Node n) {
        if (isHomogeneus(c)) {
            orden(c, n);
            Node add = new Node(c, n, n.previuos);
            add.previuos.next = add;
            add.next.previuos = add;
            size++;
            return add;
        }
        return null;
    }

    private boolean isHomogeneus(Comparable c) {
        if (size == 0) {
            return true;
        }
        //No cambiar orden de los if que llueven las excepciones
        //00
        //01
        //10
        //11
        if (header.next.info == null && c == null) {
            return true;
        }
        if (header.next.info == null && c != null) {
            return false;
        }
        if (header.next.info != null && c == null) {
            return false;
        }
        if (header.next.info.getClass() == c.getClass()) {
            return true;
        }
        return false;
    }

    public Iterator iterator() {
        return new IteradorSimple();
    }

    class IteradorSimple implements Iterable {

        public IteradorSimple() {
        }
        public Node actual = header;

        @Override
        public Iterator iterator() {
            return this;
        }

        @Override
        public boolean hasNext() {
            return actual.next != header;
        }

        @Override
        public Comparable next() {
            actual = actual.next;
            return actual.info;
        }

        @Override
        public void remove() {
            removeNode(actual);
        }
    }

    public Comparable removeLast() {
        return removeNode(header.previuos);
    }

    public Comparable removeFirst() {
        return removeNode(header.next);
    }

    private Comparable removeNode(Node eliminar) {
        if (size == 0) {
            throw new NoSuchElementException();
        }
        Comparable eliminado = eliminar.info;
        Node anterior = eliminar.previuos;
        Node posterior = eliminar.next;
        anterior.next = posterior;
        posterior.previuos = anterior;
        size--;
        return eliminado;
    }

    public Comparable remove(Comparable c) {
        return removeNode(node(indexOcurrence(c, true)));
    }

    public Comparable remove(int index) {
        if (size == 0 || index > size) 
        {
            throw new IndexOutOfBoundsException();
        }
        return removeNode(node(index));
        
    }

    public Comparable remove() {
        return removeNode(header.next);
    }

    public boolean removeFirstOcurrence(Comparable del) {
        return removeForOcurrence(del, true);
    }

    public boolean removeLasOcurrence(Comparable del) {
        return removeForOcurrence(del, false);
    }

    private boolean removeForOcurrence(Comparable del, boolean firstOrLast) {
        //problema de no poder parar el iterador
//        Iterator iter = this.iterator();
//        while (iter.hasNext()) {
//            Comparable c= iter.next();
//            if(del.compareTo(c)==0){
//                Comparable ocurrencia=c;
//                if(firstOrLast){
//                    iter.remove();
//                    return true;
//                }
//            }
//            }    
        if (!isHomogeneus(del)) {
            return false;
        }
        if (del == null) {
            //Como es una lista de elementos null
            if (firstOrLast) {
                removeFirst();
            }
            removeLast();
            return true;
        }
        Node actual = header.next;
        Node ocurrence = null;
        for (int i = 0; i < size; i++) {
            if (actual.info.compareTo(del) == 0) {
                ocurrence = actual;
                if (firstOrLast) {
                    removeNode(ocurrence);
                    return true;
                }
            }
            actual = actual.next;
        }
        if (!firstOrLast && ocurrence != null) {
            removeNode(ocurrence);
            return true;
        }
        return false;
    }

    class Node {

        Comparable info;
        Node next, previuos;

        public Node() {
        }

        public Node(Comparable info, Node next, Node previuos) {
            this.info = info;
            this.next = next;
            this.previuos = previuos;
        }
    }

    public void clear() {
        header.next = header.previuos = header;
        size = 0;
    }

    public Comparable set(int index, Comparable c) {
        if (!isHomogeneus(c)) {
            return false;
        }
        Node change = node(index);
        if (change != null) {
            Comparable old = change.info;
            change.info = c;
            return old;
        }
        return null;
    }

    private int indexOcurrence(Comparable c, boolean firstOrLast) {
        if (isHomogeneus(c)) {
            if (size == 0) {
                return -1;
            }
            if (c == null) {
                if (firstOrLast) {
                    return 0;
                }
                return size - 1;
            }
            Iterator iter = this.iterator();
            int index = 0;
            int indexSearch = -1;
            while (iter.hasNext()) {
                Comparable a = iter.next();
                if (a.compareTo(c) == 0) {
                    indexSearch = index;
                    if (firstOrLast) {
                        return indexSearch;
                    }
                }
                index++;
            }
            return indexSearch;
        }
        return -1;
    }

    public int indexOf(Comparable c) {
        /*if (isHomogeneus(c)) {
        if (size == 0) {
        return -1;
        }
        if (c == null) {
        return 0;
        }
        Iterator iter = this.iterator();
        int index = 0;
        while (iter.hasNext()) {
        Comparable a = iter.next();
        if (a.compareTo(c) == 0) {
        return index;
        }
        index++;
        }
        }
        return -1;*/
        return indexOcurrence(c, true);
    }

    public int lastIndexOf(Comparable c) {
        return indexOcurrence(c, false);

    }

    public boolean contains(Comparable c) {
        if (isHomogeneus(c)) {
            if (c == null) {
                return true;
            }
            Iterator iter = this.iterator();
            while (iter.hasNext()) {
                Comparable infoNode = iter.next();
                if (infoNode.compareTo(c) == 0) {
                    return true;
                }
                return false;
            }
        }
        return false;
    }

    public Comparable element() {
        return get(0);
    }

    public Comparable getFirst() {
        if (header.next == header) {
            throw new NoSuchElementException();
        }
        return header.next.info;
    }

    public Comparable getLast() {
        if (header.previuos == header) {
            throw new NoSuchElementException();
        }
        return header.previuos.info;
    }

    public Comparable get(int index) {
        if (size == 0 || index >= size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        Iterator iter = this.iterator();
        int j = 0;
        while (iter.hasNext()) {
            Comparable c = iter.next();
            if (j == index) {
                return c;
            }
            j++;
        }
        return null;
    }

    private Node node(int index) {
//        if (index == 0) {
//            throw new NoSuchElementException();
//        }
        if (index >= size || index < -1) {
            throw new IndexOutOfBoundsException();
        }
        Node e = header.next;
        for (int i = 0; i < size; i++) {
            if (index == i) {
                return e;
            }
            e = e.next;
        }
        return null;
    }

    public int size() {
        return size;
    }

    @Override
    public LinkedList clone() {
        LinkedList copia = new LinkedList();

        Node copiar = header.next;
        for (int i = 0; i < size; i++) {
            copia.add(copiar.info);
            copiar = copiar.next;
        }
        return copia;
    }

    public Comparable[] toArray() {
        if (size == 0) {
            return null;
        }
        Comparable[] array = new Comparable[size];
        Node actual = header.next;
        for (int i = 0; i < size; i++) {
            Comparable info = actual.info;
            array[i] = info;
            actual= actual.next;
        }
        return array;
    }
    public Comparable[] toArray(Comparable[] array) {
        if (size == 0) {
            return null;
        }
        array = new Comparable[size];
        Node actual = header.next;
        for (int i = 0; i < size; i++) {
            Comparable info = actual.info;
            array[i] = info;
            actual= actual.next;
        }
        return array;
    }

    @Override
    public String toString() {
        StringBuilder r = new StringBuilder();
        if (size == 0) {
            return "lista vacia";
        } else {
            Node e = header.next;
            for (int i = 0; i < size; i++) {
                r.append(e.info).append(" ");
                e = e.next;
            }
            return r.toString();
        }
    }
}
